Plots

A plot() produces a single visualisation as a Web element. Similar to other grammars, a plot consists of marks—graphical primitives such as bars, areas, and lines—which serve as chart layers. Plots use the semantics of Observable Plot, such that each plot has a dedicated set of encoding channels with named scale mappings such as x, y, color, opacity, etc.

Basics

Plots support faceting of the x and y dimensions, producing associated fx and fy scales. Plots are rendered to SVG output using Observable Plot.

Here is a simple dot plot that demonstrates some key concepts:

from inspect_viz import Data
from inspect_viz.plot import plot
from inspect_viz.mark import dot

penguins = Data.from_file("penguins.parquet")

plot(
    dot(penguins, x="body_mass", y="flipper_length",
        stroke="species", symbol="species"),
    legend="symbol",
    grid=True,
    width=700,
    height=400
)
1
dot() mark for a simple dot plot, using a distinct stroke and symbol to denote the “species” column.
2
Legend in the default location, keyed by symbol.
3
Additional attributes that affect plot size and appearance.
Tip

Inspect Viz is built on top of the Mosaic data visualization system which is in turn built on Observable Plot.

Below we’ll describe the the Inspect Viz Python API, which typically maps quite closely to the Observable Plot JavaScript API. Once you start creating your own plots and are using Google or an LLM to help with development, asking how to do things in Observable Plot will typically yield actionable advice.

Facets

Plots support faceting of the x and y dimensions, producing associated fx and fy scales. For example, here we comopare model performance on several tasks. The task_name is the fx scale, resulting in a separate grouping of bars for each task:

from inspect_viz import Data
from inspect_viz.plot import plot, legend
from inspect_viz.mark import bar_y

evals = Data.from_file("evals.parquet")

plot(
    bar_y( 
        evals, x="model", fx="task_name",
        y="score_headline_value",
        fill="model",
    ),
    legend=legend("color", location="bottom"),
    x_label=None, x_ticks=[], fx_label=None,
    y_label="score", y_domain=[0, 1.0]
)
1
Add an x-facet (“task_name”) using the fx option.
2
Define legend using legend() function (to enable setting location and other options).
3
Remove default x labeling as it is handled by the legend.
4
Tweak y-axis with shorter label and ensure that it goes all the way up to 1.0.

Marks

The plots above use only a single mark (dot() and bar_y() respectively). More sophisticated plots are often constructed with multiple marks. For example, here is a plot that adds a regression line mark do a standard dot plot:

from inspect_viz import Data
from inspect_viz.mark import dot, regression_y
from inspect_viz.plot import plot

athletes = Data.from_file("athletes.parquet")

plot(
    dot(athletes, x="weight", y="height", fill="sex", opacity=0.1),
    regression_y(athletes, x="weight", y="height", stroke="sex"),
    legend="color"
)
1
Use fill to distinguish male and female athletes; use opacity to deal with a large density of data points.
2
Use stroke to ensure that male and female athletes each get their own regression line.

Tooltips

Tooltips enable you to provide additional details when the user hovers their mouse over various regions of the plot. Tooltips are enabled automatically for dot marks (dot(), dot_x(), dot_y(), circle(), and hexagon()) and can be enabled with tip=True for other marks. For example:

plot(
    bar_x(penguins, x=count(), y="species", fill="species",
          tip=True
    )
)

Note that tooltips can interfere with plot interactions—for example, if your bar plot was clickable to drive selections in other plots you would not want to specify tip=True.

Values

Tooltips show the values of all columns from the dataset that provide scales (e.g. x, y, fx, stroke, fill, symbol, etc.):

You can add additional values to tooltips by specifying more channels (data table columns or transforms) in your mark. These channels won’t appear on the plot but will appear on the tooltip:

plot(
    dot(penguins, x="body_mass", y="flipper_length",  
        stroke="species", symbol="species", 
        channels={ "sex": "sex", "island": "island"}
)

Titles

Plot titles can be added using a text() mark. For example, here we add a title at the top of the frame:

from inspect_viz.mark import TextStyles, text

plot(
    text(text=["Olympic Athletes"], frame_anchor="top", 
         styles=TextStyles(font_size=16), dy=-20),
    dot(athletes, x="weight", y="height", fill="sex", opacity=0.1),
    regression_y(athletes, x="weight", y="height", stroke="sex"),
    legend="color"
)

Data

In the examples above we made Data available by reading from a parquet file. We can also read data from any Python Data Frame (e.g. Pandas, Polars, PyArrow, etc.). For example:

import pandas as pd
from inspect_viz import Data

# read directly from file
penguins = Data.from_file("penguins.parquet")

# read from Pandas DF (i.e. to preprocess first)
df = pd.read_parquet("penguins.parquet")
penguins = Data.from_dataframe(df)

You might wonder why is there a special Data class in Inspect Viz rather than using data frames directly? This is because Inpsect Viz is an interactive system where data can be dynamically filtered and transformed as part of plotting—the Data therefore needs to be sent to the web browser rather than remaining only in the Python session. This has a couple of important implications:

  1. Data transformations should be done using standard Python Data Frame operations prior to reading into Data for Inspect Viz.

  2. Since Data is embedded in the web page, you will want to filter it down to only the columns required for plotting (as you don’t want the additional columns making the web page larger than is necessary).

Data Selections

One other important thing to understand is that Data has a built in selection which is used in filtering operations on the client. This means that if you want your inputs and plots to stay synchoronized, you should pass the same Data instance to all of them (i.e. import into Data once and then share that reference). For example:

from inspect_viz import Data
from inspect_viz.plot import plot
from inspect_viz.mark import dot
from inspect_viz.input import select
from inspect_viz.layout import vconcat

# we import penguins once and then pass it to select() and dot()
penguins = Data.from_file("penguins.parquet")

vconcat( 
   select(penguins, label="Species", column="species"),
   plot(
      dot(penguins, x="body_mass", y="flipper_length",
          stroke="species", symbol="species"),
      legend="symbol",
      color_domain="fixed"  
   )
)

Attributes

Attributes are plot-level settings such as width, height, margins, and scale options (e.g., x_domain, color_range, y_tick_format). Attributes may be Param-valued, in which case a plot updates upon param changes.

Some of the more useful plot attribues include:

  • width, height, and aspect_ratio for controlling plot size.

  • margin and facet_margin (and more specific margins like margin_top) for controlling layout margins.

  • style for providing CSS styles.

  • aria_label and aria_description, x_aria_label, x_aria_description, etc. for accessibilty attributes.

  • x_domain, x_range,y_domain, andy_range` for controlling the domain and range of axes.

  • Tick settings for x, y, fx, and fy axes (e.g. x_ticks, x_tick_rotate, etc.)

  • r (radius) scale settings (e.g. r_domain, r_range, r_label, etc.)

See PlotAttributes for documentation on all available plot attributes.

Legends

Legends can be added to plot specifications or included as standalone elements.

See the legend() function documentation for details on legend options including location, columns, label text, size, and margins.

The name directive gives a plot a unique name. A standalone legend can reference a named plot legend(..., for_plot="penguins") to avoid respecifying scale domains and ranges.

Legends also act as interactors, taking a bound Selection as a target parameter. For example, discrete legends use the logic of the toggle interactor to enable point selections. Two-way binding is supported for Selections using single resolution, enabling legends and other interactors to share state.

See the docs on Toggle interactors for an example of an interactive legend.